package connection_modul;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.Vector;

import javax.swing.JOptionPane;

import parameters.RlmServerParameters;
import utils.Constants;


/**
 * Klasa obezbedjuje konekciju sa bazom.
 * @author Uros Krkic
 */
public class DatabaseModul implements Constants {

	private Connection connection;
	
	// Vektor cuva sve konekcije
	private Vector<Connection> allConnections;
	// Slobodne konekcije ka bazi
	private Vector<Connection> freeConnections;
	// Konekcije poveza sa bazom
	private Vector<Connection> usedConnections;
	
	
	// dodato
	private String driver;
	private String url;
	private String user;
	private String password;
	
	private int connectionNumber = 1;
	
		
	public DatabaseModul(String driver, String url, String user, String password, int connectionNumber, boolean isPool) {
		
		this.connectionNumber = connectionNumber;
		
		try {
			this.driver = driver;
			this.url = url;
			this.user = user;
			this.password = password;
			
			initializeVectors();
			
			Class.forName(this.driver);
			
//			connection = DriverManager.getConnection(url, user, password);
			
			/*
			 * Ukoliko je zatrazen rad u single connection rezimu
			 */
			if(!isPool)
				connection = createDBConnection();
			else /* U slucaju poolinga se kreira 10 konekcija ka bazi*/
				createConnectionPooling();
//			displayDBProperties();
			
		}
		catch (ClassNotFoundException csfe) {
			databaseConnectionError("Greska u ucitavanju drivera.");
		}
		
	}
	
	
	
	/**
	 * Konstuktor koji koristi eksterne parametre da bi kreirao konekciju ka bazi.
	 * Jedini argument je boolean koji ukazuje na pooling.
	 * @author Uros Krkic
	 */
	public DatabaseModul(int connectionNumber, boolean isPool) {
		
		this.connectionNumber = connectionNumber;
		
		this.getDBParameters();
		
		initializeVectors();
		
		try {
			Class.forName(this.driver);
			
			if ( !isPool )
				connection = createDBConnection();
			else
				createConnectionPooling();
			
//			displayDBProperties();
			
		}
		catch (ClassNotFoundException csfe) {
			databaseConnectionError("Greska u ucitavanju drivera.");
		}
	}
	
	
	public Connection getDatabaseConnection() {
		return this.connection;
	}
	
	
	public void closeDatabaseConnection() {
		try {
			if (this.connection != null)
				this.connection.close();
			this.connection = null;
		}
		catch (SQLException sqle) {
			databaseConnectionError("Greska u zatvaranju konekcije sa bazom.");
		}
	}
	
	
	private void initializeVectors() {
		this.freeConnections = new Vector<Connection>();
		this.allConnections = new Vector<Connection>();
		this.usedConnections = new Vector<Connection>();
	}
	
	/**
	 * Metoda skadisti parametre koji su potrebni za povezivanje sa bazom podataka.
	 *  
	 * @author Uros Krkic
	 */
	private void getDBParameters() {
		
		driver = RlmServerParameters.getDbDriver();
		url = RlmServerParameters.getDbUrl();
		user = RlmServerParameters.getDbUser();
		password = RlmServerParameters.getDbPassword();
	}
	
	
	
	/**
	 * Prikazuje podatke o konekciji sa bazom.
	 * @author Uros Krkic
	 */
	@SuppressWarnings("unused")
	private void displayDBProperties() {
		DatabaseMetaData metaData = null;
		ResultSet rs = null;
		int connectionNumber = 0;
		Iterator iterator = freeConnections.iterator();
		while(iterator.hasNext()) {
			Connection connection = (Connection)iterator.next();
		try {
			if (connection != null) {
				connectionNumber++;
				metaData = connection.getMetaData();				
				System.out.println("====================================================");
				System.out.println("Connection number: "+connectionNumber);
				System.out.println("Driver Information");
				System.out.println("\tDriver Name: " + metaData.getDriverName());
				System.out.println("\tDriver Version: " + metaData.getDriverVersion ());
				System.out.println("\nDatabase Information ");
				System.out.println("\tDatabase Name: " + metaData.getDatabaseProductName());
				System.out.println("\tDatabase Version: " + metaData.getDatabaseProductVersion());
				System.out.println("\nAvalilable Catalogs ");				
				rs = metaData.getCatalogs();
				
				while ( rs.next() ) {
					System.out.println("\tCatalog: " + rs.getString(1));
				}
				
				if (rs != null)
					rs.close();
				
				rs = null;
             }
			else {
				System.out.println("Error: No active Connection!");
			}
		}
		catch (SQLException sqle) {
		}
		
		metaData = null;
		} // while petlja
	}


	
	/**
	 * Kreira se konekcija sa bazom na osnovu prethodno podesenog url-a, user-a i pass-a.
	 * Rezultat je novokreirani objekat tipa Connectin. Ukoliko je pokusaj neuspesan, 
	 * vraca se null.
	 * 
	 * @return - rezultat kreiranja.
	 * @authors Uros Krkic & Martin Jondzic
	 */
	private Connection createDBConnection() {
		try {
			return DriverManager.getConnection(url, user, password);
			
		}
		catch (SQLException sqle) {
			databaseConnectionError("Greska u otvaranju konekcije sa bazom.");
		}
		return null;		
	}


	
	/**
	 * Kreira se Pooling koji otvara connectionNumber(broj konekcija,
	 * moze se menjati) konekcija ka bazi. Ukoliko je kreiranje
	 * konekcije uspesno, Connection objekat se smesta u vektor
	 * freeConnections cime se obezbedjuje njegovo eksploatisanje
	 * u trenutku trazenja usluga konekcije.
	 * 
	 * @authors Uros Krkic & Martin Jondzic
	 */
	private void createConnectionPooling() {
		
		for(int i = 0; i < connectionNumber; i++) {
			
			Connection newConnection = createDBConnection();
			try {
				newConnection.setAutoCommit(false);
				
			} catch (SQLException e) {
				e.printStackTrace();
			}
			
			if(newConnection != null) {
				freeConnections.add(newConnection);
				allConnections.add(newConnection);
			}
		}
	}

	
	
	/**
	 * Preuzima slobodnu konekciju iz pool-a. Ukoliko nema slobodnih
	 * konekcija, nit koja pristupa pool-u se stavlja u red cekanja
	 * do narednog pozivanja metode backToPool
	 * 
	 * @see backToPool()
	 * @return konekcija dobijena iz pool-a.
	 * 
	 * @authors Uros Krkic & Martin Jondzic
	 */
	public synchronized Connection retrieveConnection() {
		Connection connection = null;
		// vraca se slobodna konekcija, ukoliko je ima
		if(freeConnections.size() > 0) {
			connection = freeConnections.remove(0);
			usedConnections.add(connection);
			
			return connection;
			
		} else // inace se nit stavlja u red cekanja		
			try {
				wait();
				connection = freeConnections.remove(0);
				usedConnections.add(connection);
				
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			
		return connection;
	}
	

	
	/**
	 * Metoda vraca konekciju u pool. Nakon vracanja se poziva
	 * notify() metoda koja obavestava cekajucu nit
	 * da moze pristupiti pool-u.
	 * 
	 * @param connection - objekat konekcije koji se vraca u pool
	 * @see retrieveConnection()
	 * 
	 * @authors Uros Krkic & Martin Jondzic
	 * 
	 */
	public synchronized void backToPool(Connection connection) {
		if(connection == null)
			return;
		if(usedConnections.removeElement(connection))
			freeConnections.add(connection);
		notify();		
	}


	
	/**
	 * Zatvara sve konekcije otvorene ka bazi podataka.
	 * Poziva se iskljucivo prilikom gasenja servera.
	 * 
	 * @authors Uros Krkic & Martin Jondzic
	 */
	public void closeConnections() {
		Iterator iterator = allConnections.iterator();
		
		while(iterator.hasNext()) {
			Connection conn = (Connection)iterator.next();
			try {
				conn.close();
				
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}

	
	
	/**
	 * Panel sa porukama o greskama pri uspostavljanju konekcije sa bazom.
	 * @param error - opis nastale greske
	 * @author Uros Krkic
	 */
	private void databaseConnectionError(String error) {
		JOptionPane.showMessageDialog(null, error, "Error", JOptionPane.ERROR_MESSAGE);
	}
	
}
